Biblioteki:

library(reshape2)
library(dplyr)
library(tidyr)
library(knitr)
library(ggplot2)
library(plotly)
library(corrplot)
library(shiny)
library(reshape)
library(randomForest)
library(caret)

Wczytanie danych

Zbiór pochodzi z pliku “sledzie.csv”,zwaiera16 atrybutów opisujących 52582 obserwacji. W zbiorze znajdują się 10094 niekompletnych obserwacji. Wartości puste w zbiorze reprezentowane sa za pomocą ‘?’, a część dziesiętna liczb oddzielona jest kropką. Zbiór posiada nagłówek opisujący nazwy atrybutów. By dane zostały wczytane poprawnie, do funcji read.csv przekazano takie argumenty jak: nazwa pliku, forma reprezentacji wartości pustych, że zbiór zawiera nagłówek oraz listę prezentującą klasy danych atrybutów.

Powtarzalność wyników

W celu uzyskania powtarzalności wyników, ustawiono ziarno generatora na:

set.seed(666)

Analiza zbioru:

Na przestrzeni ostatnich lat zauważono stopniowy spadek rozmiaru śledzia oceanicznego wyławianego w Europie. Do analizy zebrano pomiary śledzi i warunków w jakich żyją z ostatnich 60 lat. Dane były pobierane z połowów komercyjnych jednostek. W ramach połowu jednej jednostki losowo wybierano od 50 do 100 sztuk trzyletnich śledzi. Dane dostępne są pod adresem: http://www.cs.put.poznan.pl/alabijak/emd/projekt/sledzie.csv.

Zbiór danych zawiera 16 atrybutów:  

length - długość złowionego śledzia [cm]
cfin1 - dostępność planktonu [zagęszczenie Calanus finmarchicus gat. 1]
cfin2 - dostępność planktonu [zagęszczenie Calanus finmarchicus gat. 2]
chel1 - dostępność planktonu [zagęszczenie Calanus helgolandicus gat. 1]
chel2 - dostępność planktonu [zagęszczenie Calanus helgolandicus gat. 2]
lcop1 - dostępność planktonu [zagęszczenie widłonogów gat. 1]
lcop2 - dostępność planktonu [zagęszczenie widłonogów gat. 2]
fbar - natężenie połowów w regionie [ułamek pozostawionego narybku]
recr - roczny narybek [liczba śledzi]
cumf - łączne roczne natężenie połowów w regionie [ułamek pozostawionego narybku]
totaln - łączna liczba ryb złowionych w ramach połowu [liczba śledzi]
sst - temperatura przy powierzchni wody [°C]
sal - poziom zasolenia wody [Knudsen ppt]
xmonth - miesiąc połowu [numer miesiąca]
nao - oscylacja północnoatlantycka [mb]

Kolejne wiersze reprezentują kolejne obserwacje i są ułożone hronologicznie.

summary(raw_df)

uniq <- raw_df %>% summarise_each(funs(n_distinct(., na.rm = TRUE)))
uniq

Długość śledzia

Długość śledzia jest wyrażona w cm.

plot_length <- ggplot(df,aes(X,length))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_length <- plot_length + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Długość śledzia [cm]") + theme_minimal() 
plot_length
plot_length_hist<- ggplot(df,aes(length)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Długość", y = "Częstość") + ggtitle("Histogram długości śledzia") 
  
plot_length_hist

Dostępność planktonu - cfin1

plot_plankton1 <- ggplot(df,aes(X,cfin1))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_plankton1 <- plot_plankton1 + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziomu dostępności planktonu cfin1") + theme_minimal() 
plot_plankton1
plot_plankton1_hist<- ggplot(df,aes(cfin1)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram dostępności cfin1") 
  
plot_plankton1_hist

Dostępność planktonu - cfin2

plot_plankton2 <- ggplot(df,aes(X,cfin2))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_plankton2 <- plot_plankton2 + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziom dostępności planktonu cfin2 od czasu") + theme_minimal() 
plot_plankton2
plot_plankton2_hist<- ggplot(df,aes(cfin2)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram dostępności cfin2") 
  
plot_plankton2_hist

Dostępność planktonu - chel1

plot_plankton3 <- ggplot(df,aes(X,chel1))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_plankton3 <- plot_plankton3 + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziom dostępności planktonu chel1 od czasu") + theme_minimal() 
plot_plankton3
plot_plankton3_hist<- ggplot(df,aes(chel1)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram dostępności chel1") 
  
plot_plankton3_hist

Dostępność planktonu - chel2

plot_plankton3 <- ggplot(df,aes(X,chel2))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_plankton3 <- plot_plankton3 + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziomu  dostępności planktonu chel2 od czasu") + theme_minimal() 
plot_plankton3
plot_plankton3_hist<- ggplot(df,aes(chel2)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram dostępności chel2") 
  
plot_plankton3_hist

Dostępność planktonu - lcop1

plot_plankton4 <- ggplot(df,aes(X,lcop1))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_plankton4 <- plot_plankton4 + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziom dostępności planktonu lcop1 od czasu") + theme_minimal() 
plot_plankton4
plot_plankton4_hist<- ggplot(df,aes(lcop1)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram dostępności lcop1") 
  
plot_plankton4_hist

Dostępność planktonu - lcop2

plot_plankton5 <- ggplot(df,aes(X,lcop2))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_plankton5 <- plot_plankton5 + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziom dostępności planktonu lcop2 od czasu") + theme_minimal() 
plot_plankton5
plot_plankton5_hist<- ggplot(df,aes(lcop2)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram dostępności lcop2") 
  
plot_plankton5_hist

Natężenie połowów w regionie - fbar


plot_polow <- ggplot(df,aes(X,fbar))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_polow <- plot_polow + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Natężenie połowów w regionie od czasu - fbar") + theme_minimal() 
plot_polow
plot_polow_hist<- ggplot(df,aes(fbar)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Natężenie", y = "Częstość") + ggtitle("Histogram natężenie połowu w rejonie -  fbar") 
  
plot_polow_hist

Poziom rocznego narybku - recr


plot_recr <- ggplot(df,aes(X,recr))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_recr <- plot_recr + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziom rocznego narybku od czasu - recr") + theme_minimal() 
plot_recr
plot_recr_hist<- ggplot(df,aes(recr)) + geom_histogram(binwidth=160000, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Roczny narybek", y = "Częstość") + ggtitle("Histogram rocznego narybku - recr") 
  
plot_recr_hist

Roczne natęzenie połowow - cumf


plot_cumf <- ggplot(df,aes(X,cumf))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_cumf <- plot_cumf + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Łączne roczne natężenie połowów w regionie") + theme_minimal() 
plot_cumf
plot_cumf_hist<- ggplot(df,aes(cumf)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram łącznego rocznego natężenia połowów w regionie - cumf") 
  
plot_cumf_hist

Liczba zlowionych ryb - totaln


plot_totaln <- ggplot(df,aes(X,totaln))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_totaln <- plot_totaln + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Liczba złowionych ryb w ramach połowu") + theme_minimal() 
plot_totaln
plot_totaln_hist<- ggplot(df,aes(totaln)) + geom_histogram(binwidth=100000, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość złowionych ryb", y = "Częstość") + ggtitle("Histogram łącznej liczby złowionych ryb w ramach połowu - totaln") 
  
plot_totaln_hist

Temperatura przy powiechni wody - sst

Atrubut sst zawiera informacje o temperaturze przy powierzchni wody, oraz jest wyrażony w stopniach Celcjusza


plot_sst <- ggplot(df,aes(X,sst))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_sst <- plot_sst + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Temperatura przy powierzchni wody [C]") + theme_minimal() 
plot_sst
plot_sst_hist<- ggplot(df,aes(sst)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Temperatura", y = "Częstość") + ggtitle("Histogram temperatury przy powierzchni wody - sst") 
  
plot_sst_hist

Poziom zasolenia wody - sal

Atrybut sal opisuje poziom zasolenia wody morskiej. Wartości wyrażone są w Knudsen ppt.


plot_sal <- ggplot(df,aes(X,sal))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_sal <- plot_sal + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Poziom zasolenia wody [ppt]") + theme_minimal() 
plot_sal
plot_sal_hist<- ggplot(df,aes(sal)) + geom_histogram(binwidth=0.05, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Poziom zasolenia", y = "Częstość") + ggtitle("Histogram poziomu zasolenia wody - sal") 
  
plot_sal_hist

Ostylacja północnoatlantycak - nao

Atrybut nao przedstawia natężenie globalnej cyrkulacji powietrza i wody oceanicznej. Jest zależny od ciśnienia, temperatury, prędkości wiatru i ilości opadów.


plot_nao <- ggplot(df,aes(X,nao))  + geom_line(alpha=0.3) + stat_smooth(method="auto",size = 2)
plot_nao <- plot_nao + theme(axis.text.x=element_blank(),axis.ticks.x=element_blank()) + labs(x="Czas", y="Oscylacja północnoatlantycka") + theme_minimal() 
plot_nao
plot_nao_hist<- ggplot(df,aes(nao)) + geom_histogram(binwidth=0.5, color="white", fill="steelblue") +
  theme_minimal() + labs(x = "Ilość", y = "Częstość") + ggtitle("Histogram oscylacji północnoatlantyckiej - nao") 
  
plot_nao_hist

Analiza brakujących wartości.

Brakujące wartości występują tylko w 7 atrybutach:
- dostępność planktonu -> cfin1, cfin2, chel1, chel2, lcop1, lcop2,
- temperatura przy powierzchni wody -> sst.
Łącznie niepełnych obserwacji mamy: 10094, czyli jest to 19.1966833 % całego zbiioru. Jest to znacząca część zbioru. Jednak zakładamy, że zbiór danych zawiera chronologiczne ułożone dane, dlatego proste operacje na wartościach pustych, takie jak średnia lub mediana z danego atrybutu, mogą całkowicie przekłamać nam rozkład danych. Ze względu na ilość tych danych usunięcie ich może róWnież spowodować różne skutki. Jednak dzięki temu można uznać, że operacje która została dokonana na wartościach pustych, miała jakikolwiek pozytywny lub negatywny na dalsze przetwarzanie.

for (i in 1:ncol(df)){df[is.na(df[,i]),i] <- mean(df[,i],na.rm = T)}

Korelacja między atrybutami

correlation <- cor(df_no_na)
corrplot(correlation,type="upper",tl.col = "black", tl.srt = 45)

Największą korelację można zaobserwować między parami: chel1 - loop1 ,chel2 - loop2, fbar - cumf, cfin2 - lcop2 oraz cumf - totaln. W stosunku do atrybutu ‘length’ najwyższą korelacje można zaobserwować z sst, nao. Nao jest to Oscylacja Północnoatlantycka, która jest zjawiskiem meteorologicznym. Występuje w obszarze północnego atlantyku i ma wpływ na klimat otaczających go kontynentów. Jej działalność związana jest z cyrkulacją powietrza i wody, co by tłumaczyło zależność długości śledzia od tych dwóch atrybutów na raz. length jest także skorelowany z intensywnościa połowów oraz dostępnością planktonu (chel1 - Calanus helgolandicus gat. 1 i lcop1 - widłonogów gat. 1).

Zmiana rozmiaru śledzia w czasie

partition <- createDataPartition(y=df_no_na$length, p=.05, list=FALSE)
dfPartition <- df_no_na[partition, ]
p <- ggplot(dfPartition, aes(x=X, y=length)) + geom_point() + geom_smooth() + theme_bw()
ggplotly(p)
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

Regresor

Sekcję próbującą stworzyć regresor przewidujący rozmiar śledzia (w tej sekcji należy wykorzystać wiedzę z pozostałych punktów oraz wykonać dodatkowe czynności, które mogą poprawić trafność predykcji); dobór parametrów modelu oraz oszacowanie jego skuteczności powinny zostać wykonane za pomocą techniki podziału zbioru na dane uczące, walidujące i testowe; trafność regresji powinna zostać oszacowana na podstawie miar R2 i RMSE.

Dane zostały podzielone na 2 zbiory: zbiór treningowy - zawierający 80 % danych, oraz zbiór testowy - zawierający 20 %

afterFeatureSelection <- df_no_na %>% select(length, cfin1, cfin2, chel1, chel2, lcop1, lcop2, fbar, recr, cumf, totaln, sst, sal, nao)

inTraining <- createDataPartition(y=afterFeatureSelection$length,p=0.8,list = F)
training <- afterFeatureSelection[inTraining,]
testing <- afterFeatureSelection[-inTraining,]
X_train = select(training, -length)
y_train = training$length
X_test = select(testing, -length)
y_test = testing$length

COS O MODELU

fit <- train(X_train,y_train,
            method = 'rf',
             trControl = ctrl,
             metric = "RMSE",
             tuneGrid=rfGrid,
             importance = TRUE,
             ntree=20)
ggplot(fit) + ggtitle("Optymalizacja parametru mtry na podstawie miary RMSE") + theme_bw()

Sprawdzenie modelu na danych testowych

predictions <- predict(fit, newdata = X_test)
modelValues <- data.frame(obs = y_test, pred = predictions)
fit$results
Model regresyjny został poddany testom na danych testowych. Osiągnięto wynik w postaci miar RMSE i Rsquared:
xs <- 1:length(y_test)
dataf <- data.frame(xs,y_test)
reg_plot_test <- ggplot(dataf,aes(dataf$xs,dataf$y_test)) + 
  geom_point(alpha = 0.5, size = 0.7,color="blue") + 
  geom_smooth(aes(y=dataf$y_test,colour = "trend"), size = 2) +
  coord_cartesian(ylim = c(19, 31)) +
  theme_minimal() + theme(legend.position = "none") +
  labs(x = "Nr przykładu", y = "Długość śledzia ") + 
  ggtitle("Wykres długości śledzia od czasu - prawdziwe dane") 
reg_plot_test

xs <- 1:length(predictions)
dataf <- data.frame(xs,predictions)
reg_plot_test <- ggplot(dataf,aes(dataf$xs,dataf$predictions)) + 
  geom_point(alpha = 0.5, size = 0.7,color="blue") + 
  geom_smooth(aes(y=dataf$predictions,colour = "trend"), size = 2) +
  coord_cartesian(ylim = c(19, 31)) +
  theme_minimal() + theme(legend.position = "none") +
  labs(x = "Nr przykładu", y = "Długość śledzia ") + 
  ggtitle("Wykres długości śledzia od czasu - predykcja") 
reg_plot_test

Analiza ważności atrybutów

importance <- varImp(fit, scale = FALSE)
ggplot(importance)

Z wykresu wynika, że najważniejszym atrybutem jest sst - temperatura przy powierzchni wody. Jednak kolejne 3 najważniejsze atrybuty to poziom 3 różnych rodzajów planktonu - lcop2, cfin2 oraz chel2. Jednak trzeba pamiętać o wysokiej korelacji między cfin2 a loo2, co mogło zadecydować, że oba atrybuty się znalazły jako najważniejsze.

Zależność długości śledzia od temperatury przy powiechni

temp_df <- df_no_na %>% select(X,sst,length)
temp_df2 <- melt(temp_df,id.vars = 'X')
fin_plot_tlen <- ggplot(temp_df2,aes(x = X, y=value)) + 
  geom_point(size = 0.5, alpha = 0.5, color="grey") +
  geom_smooth(size = 2) +
  facet_grid(variable~. ,scales = 'free')  + theme_minimal() + 
  labs(x = "Czas", y = "Temperatura / Długość") + 
  scale_x_continuous() + 
  ggtitle("Wykresy temperatury oraz długości śledzia od czasu") 
fin_plot_tlen

df_planct <- df_no_na %>% select(X,lcop2,cfin2,chel2,length)
df_planct2 <- melt(df_planct,id.vars = 'X')
fin_plot_tlen <- ggplot(df_planct2,aes(x = X, y=value)) + 
  geom_point(size = 0.5, alpha = 0.5, color="grey") +
  geom_smooth(size = 2) +
  facet_grid(variable~. ,scales = 'free')  + theme_minimal() + 
  labs(x = "Czas", y = "Plantkon / Długość") + 
  scale_x_continuous() + 
  ggtitle("Wykresy ilości planktonu oraz długości śledzia od czasu") 
fin_plot_tlen

Wnioski

Interaktywny Wykres

W celu wygenerowania interaktywnego wykresu za pomocą biblioteki shiny, dane z kolumny lenght zostały pogrupowane po 50 kolejnych elementów, a z takich grup policzona średnia. Wykonano to by “suwak” na wykresie nie był za długi. Niestety biblioteka shiny nie daje możliwości na wykorzystanie jej w statycznym pliku raportu, dlatego kod do wykonania takiego wykresu znajduje się w pliku “library.R”.

LS0tDQp0aXRsZTogIlJhcG9ydCINCmF1dGhvcjogJzEyNzMxMCcNCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0Og0KICBnaXRodWJfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMgQmlibGlvdGVraToNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9Rn0NCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShyZXNoYXBlKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQpsaWJyYXJ5KGNhcmV0KQ0KDQpgYGANCg0KIyBXY3p5dGFuaWUgZGFueWNoDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KZGYgPC0gcmVhZC5jc3YoInNsZWR6aWUuY3N2IixoZWFkZXI9VFJVRSxjb2xDbGFzc2VzID0gYygiaW50ZWdlciIsIm51bWVyaWMiLCJudW1lcmljIiwibnVtZXJpYyIsIm51bWVyaWMiLCJudW1lcmljIiwibnVtZXJpYyIsIm51bWVyaWMiLCJudW1lcmljIiwibnVtZXJpYyIsIm51bWVyaWMiLCJudW1lcmljIiwibnVtZXJpYyIsIm51bWVyaWMiLCJpbnRlZ2VyIiwibnVtZXJpYyIpLG5hLnN0cmluZ3MgPSAiPyIpDQoNCmRmIDwtIGRhdGEuZnJhbWUoZGYpDQpkZl9ub19uYSA8LSBuYS5vbWl0KGRmKQ0KcmF3X2RmIDwtIGRmDQoNCm5yb3dzIDwtIG5yb3coZGYpDQpuY29scyA8LSBuY29sKGRmKQ0KY29tcGxldGVfcm93cyA8LSBzdW0oY29tcGxldGUuY2FzZXMocmF3X2RmKSkNCm5fbmFfcm93cyA8LSBucm93cyAtIGNvbXBsZXRlX3Jvd3MNCmBgYA0KWmJpw7NyIHBvY2hvZHppIHogcGxpa3UgInNsZWR6aWUuY3N2Iix6d2FpZXJhYHIgbmNvbHNgIGF0cnlidXTDs3cgb3Bpc3VqxIVjeWNoIGByIG5yb3dzYCBvYnNlcndhY2ppLiBXIHpiaW9yemUgem5hamR1asSFIHNpxJkgYHIgbl9uYV9yb3dzYCBuaWVrb21wbGV0bnljaCBvYnNlcndhY2ppLiBXYXJ0b8WbY2kgcHVzdGUgdyB6YmlvcnplIHJlcHJlemVudG93YW5lIHNhIHphIHBvbW9jxIUgJz8nLCBhIGN6xJnFm8SHIGR6aWVzacSZdG5hIGxpY3piIG9kZHppZWxvbmEgamVzdCBrcm9wa8SFLiBaYmnDs3IgcG9zaWFkYSBuYWfFgsOzd2VrIG9waXN1asSFY3kgbmF6d3kgYXRyeWJ1dMOzdy4gIEJ5IGRhbmUgem9zdGHFgnkgd2N6eXRhbmUgcG9wcmF3bmllLCBkbyBmdW5jamkgcmVhZC5jc3YgcHJ6ZWthemFubyB0YWtpZSBhcmd1bWVudHkgamFrOiBuYXp3YSBwbGlrdSwgZm9ybWEgcmVwcmV6ZW50YWNqaSB3YXJ0b8WbY2kgcHVzdHljaCwgxbxlIHpiacOzciB6YXdpZXJhIG5hZ8WCw7N3ZWsgb3JheiBsaXN0xJkgcHJlemVudHVqxIVjxIUga2xhc3kgZGFueWNoIGF0cnlidXTDs3cuDQoNCiMgUG93dGFyemFsbm/Fm8SHIHd5bmlrw7N3DQpXIGNlbHUgdXp5c2thbmlhIHBvd3RhcnphbG5vxZtjaSB3eW5pa8OzdywgdXN0YXdpb25vIHppYXJubyBnZW5lcmF0b3JhIG5hOg0KYGBge3J9DQpzZXQuc2VlZCg2NjYpDQpgYGANCg0KDQojIEFuYWxpemEgemJpb3J1Og0KTmEgcHJ6ZXN0cnplbmkgb3N0YXRuaWNoIGxhdCB6YXV3YcW8b25vIHN0b3BuaW93eSBzcGFkZWsgcm96bWlhcnUgxZtsZWR6aWEgb2NlYW5pY3puZWdvIHd5xYJhd2lhbmVnbyB3IEV1cm9waWUuIERvIGFuYWxpenkgemVicmFubyBwb21pYXJ5IMWbbGVkemkgaSB3YXJ1bmvDs3cgdyBqYWtpY2ggxbx5asSFIHogb3N0YXRuaWNoIDYwIGxhdC4gRGFuZSBiecWCeSBwb2JpZXJhbmUgeiBwb8WCb3fDs3cga29tZXJjeWpueWNoIGplZG5vc3Rlay4gVyByYW1hY2ggcG/Fgm93dSBqZWRuZWogamVkbm9zdGtpIGxvc293byB3eWJpZXJhbm8gb2QgNTAgZG8gMTAwIHN6dHVrIHRyenlsZXRuaWNoIMWbbGVkemkuIERhbmUgZG9zdMSZcG5lIHPEhSBwb2QgYWRyZXNlbTogaHR0cDovL3d3dy5jcy5wdXQucG96bmFuLnBsL2FsYWJpamFrL2VtZC9wcm9qZWt0L3NsZWR6aWUuY3N2Lg0KDQpaYmnDs3IgZGFueWNoIHphd2llcmEgYHIgbmNvbHNgIGF0cnlidXTDs3c6IFwgDQoNCmxlbmd0aCAtIGTFgnVnb8WbxIcgesWCb3dpb25lZ28gxZtsZWR6aWEgW2NtXSBcDQpjZmluMSAgLSBkb3N0xJlwbm/Fm8SHIHBsYW5rdG9udSBbemFnxJlzemN6ZW5pZSBDYWxhbnVzIGZpbm1hcmNoaWN1cyBnYXQuIDFdIFwNCmNmaW4yICAtIGRvc3TEmXBub8WbxIcgcGxhbmt0b251IFt6YWfEmXN6Y3plbmllIENhbGFudXMgZmlubWFyY2hpY3VzIGdhdC4gMl0gXA0KY2hlbDEgIC0gZG9zdMSZcG5vxZvEhyBwbGFua3RvbnUgW3phZ8SZc3pjemVuaWUgQ2FsYW51cyBoZWxnb2xhbmRpY3VzIGdhdC4gMV0gXA0KY2hlbDIgIC0gZG9zdMSZcG5vxZvEhyBwbGFua3RvbnUgW3phZ8SZc3pjemVuaWUgQ2FsYW51cyBoZWxnb2xhbmRpY3VzIGdhdC4gMl0gXA0KbGNvcDEgIC0gZG9zdMSZcG5vxZvEhyBwbGFua3RvbnUgW3phZ8SZc3pjemVuaWUgd2lkxYJvbm9nw7N3ICBnYXQuIDFdIFwNCmxjb3AyICAtIGRvc3TEmXBub8WbxIcgcGxhbmt0b251IFt6YWfEmXN6Y3plbmllIHdpZMWCb25vZ8OzdyAgZ2F0LiAyXSBcDQpmYmFyICAgLSBuYXTEmcW8ZW5pZSBwb8WCb3fDs3cgdyByZWdpb25pZSBbdcWCYW1layBwb3pvc3Rhd2lvbmVnbyBuYXJ5Ymt1XSBcDQpyZWNyICAgLSByb2N6bnkgbmFyeWJlayBbbGljemJhIMWbbGVkemldIFwNCmN1bWYgICAtIMWCxIVjem5lIHJvY3puZSBuYXTEmcW8ZW5pZSBwb8WCb3fDs3cgdyByZWdpb25pZSBbdcWCYW1layBwb3pvc3Rhd2lvbmVnbyBuYXJ5Ymt1XSBcDQp0b3RhbG4gLSDFgsSFY3puYSBsaWN6YmEgcnliIHrFgm93aW9ueWNoIHcgcmFtYWNoIHBvxYJvd3UgW2xpY3piYSDFm2xlZHppXSBcDQpzc3QgICAgLSB0ZW1wZXJhdHVyYSBwcnp5IHBvd2llcnpjaG5pIHdvZHkgW8KwQ10gXA0Kc2FsICAgIC0gcG96aW9tIHphc29sZW5pYSB3b2R5IFtLbnVkc2VuIHBwdF0gXA0KeG1vbnRoIC0gbWllc2nEhWMgcG/Fgm93dSBbbnVtZXIgbWllc2nEhWNhXSBcDQpuYW8gICAgLSBvc2N5bGFjamEgcMOzxYJub2Nub2F0bGFudHlja2EgW21iXSBcDQoNCktvbGVqbmUgd2llcnN6ZSByZXByZXplbnR1asSFIGtvbGVqbmUgb2JzZXJ3YWNqZSBpIHPEhSB1xYJvxbxvbmUgaHJvbm9sb2dpY3puaWUuIA0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0Kc3VtbWFyeShyYXdfZGYpDQoNCnVuaXEgPC0gcmF3X2RmICU+JSBzdW1tYXJpc2VfZWFjaChmdW5zKG5fZGlzdGluY3QoLiwgbmEucm0gPSBUUlVFKSkpDQp1bmlxDQpgYGANCg0KIyMgRMWCdWdvxZvEhyDFm2xlZHppYSAgDQpExYJ1Z2/Fm8SHIMWbbGVkemlhIGplc3Qgd3lyYcW8b25hIHcgY20uDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KcGxvdF9sZW5ndGggPC0gZ2dwbG90KGRmLGFlcyhYLGxlbmd0aCkpICArIGdlb21fbGluZShhbHBoYT0wLjMpICsgc3RhdF9zbW9vdGgobWV0aG9kPSJhdXRvIixzaXplID0gMikNCnBsb3RfbGVuZ3RoIDwtIHBsb3RfbGVuZ3RoICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iRMWCdWdvxZvEhyDFm2xlZHppYSBbY21dIikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X2xlbmd0aA0KcGxvdF9sZW5ndGhfaGlzdDwtIGdncGxvdChkZixhZXMobGVuZ3RoKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0wLjUsIGNvbG9yPSJ3aGl0ZSIsIGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgbGFicyh4ID0gIkTFgnVnb8WbxIciLCB5ID0gIkN6xJlzdG/Fm8SHIikgKyBnZ3RpdGxlKCJIaXN0b2dyYW0gZMWCdWdvxZtjaSDFm2xlZHppYSIpIA0KICANCnBsb3RfbGVuZ3RoX2hpc3QNCmBgYA0KDQojIyBEb3N0xJlwbm/Fm8SHIHBsYW5rdG9udSAtIGNmaW4xDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KcGxvdF9wbGFua3RvbjEgPC0gZ2dwbG90KGRmLGFlcyhYLGNmaW4xKSkgICsgZ2VvbV9saW5lKGFscGhhPTAuMykgKyBzdGF0X3Ntb290aChtZXRob2Q9ImF1dG8iLHNpemUgPSAyKQ0KcGxvdF9wbGFua3RvbjEgPC0gcGxvdF9wbGFua3RvbjEgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKyBsYWJzKHg9IkN6YXMiLCB5PSJQb3ppb211IGRvc3TEmXBub8WbY2kgcGxhbmt0b251IGNmaW4xIikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X3BsYW5rdG9uMQ0KcGxvdF9wbGFua3RvbjFfaGlzdDwtIGdncGxvdChkZixhZXMoY2ZpbjEpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuNSwgY29sb3I9IndoaXRlIiwgZmlsbD0ic3RlZWxibHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHggPSAiSWxvxZvEhyIsIHkgPSAiQ3rEmXN0b8WbxIciKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBkb3N0xJlwbm/Fm2NpIGNmaW4xIikgDQogIA0KcGxvdF9wbGFua3RvbjFfaGlzdA0KYGBgDQoNCiMjIERvc3TEmXBub8WbxIcgcGxhbmt0b251IC0gY2ZpbjINCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCnBsb3RfcGxhbmt0b24yIDwtIGdncGxvdChkZixhZXMoWCxjZmluMikpICArIGdlb21fbGluZShhbHBoYT0wLjMpICsgc3RhdF9zbW9vdGgobWV0aG9kPSJhdXRvIixzaXplID0gMikNCnBsb3RfcGxhbmt0b24yIDwtIHBsb3RfcGxhbmt0b24yICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iUG96aW9tIGRvc3TEmXBub8WbY2kgcGxhbmt0b251IGNmaW4yIG9kIGN6YXN1IikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X3BsYW5rdG9uMg0KcGxvdF9wbGFua3RvbjJfaGlzdDwtIGdncGxvdChkZixhZXMoY2ZpbjIpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuNSwgY29sb3I9IndoaXRlIiwgZmlsbD0ic3RlZWxibHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHggPSAiSWxvxZvEhyIsIHkgPSAiQ3rEmXN0b8WbxIciKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBkb3N0xJlwbm/Fm2NpIGNmaW4yIikgDQogIA0KcGxvdF9wbGFua3RvbjJfaGlzdA0KYGBgDQoNCiMjIERvc3TEmXBub8WbxIcgcGxhbmt0b251IC0gY2hlbDENCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCnBsb3RfcGxhbmt0b24zIDwtIGdncGxvdChkZixhZXMoWCxjaGVsMSkpICArIGdlb21fbGluZShhbHBoYT0wLjMpICsgc3RhdF9zbW9vdGgobWV0aG9kPSJhdXRvIixzaXplID0gMikNCnBsb3RfcGxhbmt0b24zIDwtIHBsb3RfcGxhbmt0b24zICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iUG96aW9tIGRvc3TEmXBub8WbY2kgcGxhbmt0b251IGNoZWwxIG9kIGN6YXN1IikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X3BsYW5rdG9uMw0KcGxvdF9wbGFua3RvbjNfaGlzdDwtIGdncGxvdChkZixhZXMoY2hlbDEpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuNSwgY29sb3I9IndoaXRlIiwgZmlsbD0ic3RlZWxibHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHggPSAiSWxvxZvEhyIsIHkgPSAiQ3rEmXN0b8WbxIciKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBkb3N0xJlwbm/Fm2NpIGNoZWwxIikgDQogIA0KcGxvdF9wbGFua3RvbjNfaGlzdA0KYGBgDQoNCiMjIERvc3TEmXBub8WbxIcgcGxhbmt0b251IC0gY2hlbDINCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCnBsb3RfcGxhbmt0b24zIDwtIGdncGxvdChkZixhZXMoWCxjaGVsMikpICArIGdlb21fbGluZShhbHBoYT0wLjMpICsgc3RhdF9zbW9vdGgobWV0aG9kPSJhdXRvIixzaXplID0gMikNCnBsb3RfcGxhbmt0b24zIDwtIHBsb3RfcGxhbmt0b24zICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iUG96aW9tdSAgZG9zdMSZcG5vxZtjaSBwbGFua3RvbnUgY2hlbDIgb2QgY3phc3UiKSArIHRoZW1lX21pbmltYWwoKSANCnBsb3RfcGxhbmt0b24zDQpwbG90X3BsYW5rdG9uM19oaXN0PC0gZ2dwbG90KGRmLGFlcyhjaGVsMikpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC41LCBjb2xvcj0id2hpdGUiLCBmaWxsPSJzdGVlbGJsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeCA9ICJJbG/Fm8SHIiwgeSA9ICJDesSZc3RvxZvEhyIpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIGRvc3TEmXBub8WbY2kgY2hlbDIiKSANCiAgDQpwbG90X3BsYW5rdG9uM19oaXN0DQpgYGANCg0KIyMgRG9zdMSZcG5vxZvEhyBwbGFua3RvbnUgLSBsY29wMQ0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KcGxvdF9wbGFua3RvbjQgPC0gZ2dwbG90KGRmLGFlcyhYLGxjb3AxKSkgICsgZ2VvbV9saW5lKGFscGhhPTAuMykgKyBzdGF0X3Ntb290aChtZXRob2Q9ImF1dG8iLHNpemUgPSAyKQ0KcGxvdF9wbGFua3RvbjQgPC0gcGxvdF9wbGFua3RvbjQgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKyBsYWJzKHg9IkN6YXMiLCB5PSJQb3ppb20gZG9zdMSZcG5vxZtjaSBwbGFua3RvbnUgbGNvcDEgb2QgY3phc3UiKSArIHRoZW1lX21pbmltYWwoKSANCnBsb3RfcGxhbmt0b240DQpwbG90X3BsYW5rdG9uNF9oaXN0PC0gZ2dwbG90KGRmLGFlcyhsY29wMSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC41LCBjb2xvcj0id2hpdGUiLCBmaWxsPSJzdGVlbGJsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeCA9ICJJbG/Fm8SHIiwgeSA9ICJDesSZc3RvxZvEhyIpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIGRvc3TEmXBub8WbY2kgbGNvcDEiKSANCiAgDQpwbG90X3BsYW5rdG9uNF9oaXN0DQpgYGANCg0KIyMgRG9zdMSZcG5vxZvEhyBwbGFua3RvbnUgLSBsY29wMg0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KcGxvdF9wbGFua3RvbjUgPC0gZ2dwbG90KGRmLGFlcyhYLGxjb3AyKSkgICsgZ2VvbV9saW5lKGFscGhhPTAuMykgKyBzdGF0X3Ntb290aChtZXRob2Q9ImF1dG8iLHNpemUgPSAyKQ0KcGxvdF9wbGFua3RvbjUgPC0gcGxvdF9wbGFua3RvbjUgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKyBsYWJzKHg9IkN6YXMiLCB5PSJQb3ppb20gZG9zdMSZcG5vxZtjaSBwbGFua3RvbnUgbGNvcDIgb2QgY3phc3UiKSArIHRoZW1lX21pbmltYWwoKSANCnBsb3RfcGxhbmt0b241DQpwbG90X3BsYW5rdG9uNV9oaXN0PC0gZ2dwbG90KGRmLGFlcyhsY29wMikpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC41LCBjb2xvcj0id2hpdGUiLCBmaWxsPSJzdGVlbGJsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeCA9ICJJbG/Fm8SHIiwgeSA9ICJDesSZc3RvxZvEhyIpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIGRvc3TEmXBub8WbY2kgbGNvcDIiKSANCiAgDQpwbG90X3BsYW5rdG9uNV9oaXN0DQpgYGANCg0KIyMgTmF0xJnFvGVuaWUgcG/Fgm93w7N3IHcgcmVnaW9uaWUgLSBmYmFyIA0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KDQpwbG90X3BvbG93IDwtIGdncGxvdChkZixhZXMoWCxmYmFyKSkgICsgZ2VvbV9saW5lKGFscGhhPTAuMykgKyBzdGF0X3Ntb290aChtZXRob2Q9ImF1dG8iLHNpemUgPSAyKQ0KcGxvdF9wb2xvdyA8LSBwbG90X3BvbG93ICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iTmF0xJnFvGVuaWUgcG/Fgm93w7N3IHcgcmVnaW9uaWUgb2QgY3phc3UgLSBmYmFyIikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X3BvbG93DQpwbG90X3BvbG93X2hpc3Q8LSBnZ3Bsb3QoZGYsYWVzKGZiYXIpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuNSwgY29sb3I9IndoaXRlIiwgZmlsbD0ic3RlZWxibHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHggPSAiTmF0xJnFvGVuaWUiLCB5ID0gIkN6xJlzdG/Fm8SHIikgKyBnZ3RpdGxlKCJIaXN0b2dyYW0gbmF0xJnFvGVuaWUgcG/Fgm93dSB3IHJlam9uaWUgLSAgZmJhciIpIA0KICANCnBsb3RfcG9sb3dfaGlzdA0KYGBgDQoNCiMjIFBvemlvbSByb2N6bmVnbyBuYXJ5Ymt1IC0gcmVjcg0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KDQpwbG90X3JlY3IgPC0gZ2dwbG90KGRmLGFlcyhYLHJlY3IpKSAgKyBnZW9tX2xpbmUoYWxwaGE9MC4zKSArIHN0YXRfc21vb3RoKG1ldGhvZD0iYXV0byIsc2l6ZSA9IDIpDQpwbG90X3JlY3IgPC0gcGxvdF9yZWNyICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iUG96aW9tIHJvY3puZWdvIG5hcnlia3Ugb2QgY3phc3UgLSByZWNyIikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X3JlY3INCnBsb3RfcmVjcl9oaXN0PC0gZ2dwbG90KGRmLGFlcyhyZWNyKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xNjAwMDAsIGNvbG9yPSJ3aGl0ZSIsIGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgbGFicyh4ID0gIlJvY3pueSBuYXJ5YmVrIiwgeSA9ICJDesSZc3RvxZvEhyIpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIHJvY3puZWdvIG5hcnlia3UgLSByZWNyIikgDQogIA0KcGxvdF9yZWNyX2hpc3QNCmBgYA0KDQojIyBSb2N6bmUgbmF0xJl6ZW5pZSBwb8WCb3dvdyAtIGN1bWYNCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCg0KcGxvdF9jdW1mIDwtIGdncGxvdChkZixhZXMoWCxjdW1mKSkgICsgZ2VvbV9saW5lKGFscGhhPTAuMykgKyBzdGF0X3Ntb290aChtZXRob2Q9ImF1dG8iLHNpemUgPSAyKQ0KcGxvdF9jdW1mIDwtIHBsb3RfY3VtZiArIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSxheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArIGxhYnMoeD0iQ3phcyIsIHk9IsWBxIVjem5lIHJvY3puZSBuYXTEmcW8ZW5pZSBwb8WCb3fDs3cgdyByZWdpb25pZSIpICsgdGhlbWVfbWluaW1hbCgpIA0KcGxvdF9jdW1mDQpwbG90X2N1bWZfaGlzdDwtIGdncGxvdChkZixhZXMoY3VtZikpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC41LCBjb2xvcj0id2hpdGUiLCBmaWxsPSJzdGVlbGJsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeCA9ICJJbG/Fm8SHIiwgeSA9ICJDesSZc3RvxZvEhyIpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIMWCxIVjem5lZ28gcm9jem5lZ28gbmF0xJnFvGVuaWEgcG/Fgm93w7N3IHcgcmVnaW9uaWUgLSBjdW1mIikgDQogIA0KcGxvdF9jdW1mX2hpc3QNCmBgYA0KDQojIyBMaWN6YmEgemxvd2lvbnljaCByeWIgLSB0b3RhbG4NCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCg0KcGxvdF90b3RhbG4gPC0gZ2dwbG90KGRmLGFlcyhYLHRvdGFsbikpICArIGdlb21fbGluZShhbHBoYT0wLjMpICsgc3RhdF9zbW9vdGgobWV0aG9kPSJhdXRvIixzaXplID0gMikNCnBsb3RfdG90YWxuIDwtIHBsb3RfdG90YWxuICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iTGljemJhIHrFgm93aW9ueWNoIHJ5YiB3IHJhbWFjaCBwb8WCb3d1IikgKyB0aGVtZV9taW5pbWFsKCkgDQpwbG90X3RvdGFsbg0KcGxvdF90b3RhbG5faGlzdDwtIGdncGxvdChkZixhZXModG90YWxuKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xMDAwMDAsIGNvbG9yPSJ3aGl0ZSIsIGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgbGFicyh4ID0gIklsb8WbxIcgesWCb3dpb255Y2ggcnliIiwgeSA9ICJDesSZc3RvxZvEhyIpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIMWCxIVjem5laiBsaWN6YnkgesWCb3dpb255Y2ggcnliIHcgcmFtYWNoIHBvxYJvd3UgLSB0b3RhbG4iKSANCiAgDQpwbG90X3RvdGFsbl9oaXN0DQpgYGANCg0KIyMgVGVtcGVyYXR1cmEgcHJ6eSBwb3dpZWNobmkgd29keSAtIHNzdA0KQXRydWJ1dCBzc3QgemF3aWVyYSBpbmZvcm1hY2plIG8gdGVtcGVyYXR1cnplIHByenkgcG93aWVyemNobmkgd29keSwgb3JheiBqZXN0IHd5cmHFvG9ueSB3IHN0b3BuaWFjaCBDZWxjanVzemENCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCg0KcGxvdF9zc3QgPC0gZ2dwbG90KGRmLGFlcyhYLHNzdCkpICArIGdlb21fbGluZShhbHBoYT0wLjMpICsgc3RhdF9zbW9vdGgobWV0aG9kPSJhdXRvIixzaXplID0gMikNCnBsb3Rfc3N0IDwtIHBsb3Rfc3N0ICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh4PSJDemFzIiwgeT0iVGVtcGVyYXR1cmEgcHJ6eSBwb3dpZXJ6Y2huaSB3b2R5IFtDXSIpICsgdGhlbWVfbWluaW1hbCgpIA0KcGxvdF9zc3QNCnBsb3Rfc3N0X2hpc3Q8LSBnZ3Bsb3QoZGYsYWVzKHNzdCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC41LCBjb2xvcj0id2hpdGUiLCBmaWxsPSJzdGVlbGJsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeCA9ICJUZW1wZXJhdHVyYSIsIHkgPSAiQ3rEmXN0b8WbxIciKSArIGdndGl0bGUoIkhpc3RvZ3JhbSB0ZW1wZXJhdHVyeSBwcnp5IHBvd2llcnpjaG5pIHdvZHkgLSBzc3QiKSANCiAgDQpwbG90X3NzdF9oaXN0DQpgYGANCg0KIyMgUG96aW9tIHphc29sZW5pYSB3b2R5IC0gc2FsDQpBdHJ5YnV0IHNhbCBvcGlzdWplIHBvemlvbSB6YXNvbGVuaWEgd29keSBtb3Jza2llai4gV2FydG/Fm2NpIHd5cmHFvG9uZSBzxIUgdyBLbnVkc2VuIHBwdC4NCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQoNCnBsb3Rfc2FsIDwtIGdncGxvdChkZixhZXMoWCxzYWwpKSAgKyBnZW9tX2xpbmUoYWxwaGE9MC4zKSArIHN0YXRfc21vb3RoKG1ldGhvZD0iYXV0byIsc2l6ZSA9IDIpDQpwbG90X3NhbCA8LSBwbG90X3NhbCArIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSxheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArIGxhYnMoeD0iQ3phcyIsIHk9IlBvemlvbSB6YXNvbGVuaWEgd29keSBbcHB0XSIpICsgdGhlbWVfbWluaW1hbCgpIA0KcGxvdF9zYWwNCnBsb3Rfc2FsX2hpc3Q8LSBnZ3Bsb3QoZGYsYWVzKHNhbCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC4wNSwgY29sb3I9IndoaXRlIiwgZmlsbD0ic3RlZWxibHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHggPSAiUG96aW9tIHphc29sZW5pYSIsIHkgPSAiQ3rEmXN0b8WbxIciKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBwb3ppb211IHphc29sZW5pYSB3b2R5IC0gc2FsIikgDQogIA0KcGxvdF9zYWxfaGlzdA0KYGBgDQoNCiMjIE9zdHlsYWNqYSBww7PFgm5vY25vYXRsYW50eWNhayAtIG5hbw0KQXRyeWJ1dCBuYW8gcHJ6ZWRzdGF3aWEgbmF0xJnFvGVuaWUgZ2xvYmFsbmVqIGN5cmt1bGFjamkgcG93aWV0cnphIGkgd29keSBvY2Vhbmljem5lai4gSmVzdCB6YWxlxbxueSBvZCBjacWbbmllbmlhLCB0ZW1wZXJhdHVyeSwgcHLEmWRrb8WbY2kgd2lhdHJ1IGkgaWxvxZtjaSBvcGFkw7N3Lg0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KDQpwbG90X25hbyA8LSBnZ3Bsb3QoZGYsYWVzKFgsbmFvKSkgICsgZ2VvbV9saW5lKGFscGhhPTAuMykgKyBzdGF0X3Ntb290aChtZXRob2Q9ImF1dG8iLHNpemUgPSAyKQ0KcGxvdF9uYW8gPC0gcGxvdF9uYW8gKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKyBsYWJzKHg9IkN6YXMiLCB5PSJPc2N5bGFjamEgcMOzxYJub2Nub2F0bGFudHlja2EiKSArIHRoZW1lX21pbmltYWwoKSANCnBsb3RfbmFvDQpwbG90X25hb19oaXN0PC0gZ2dwbG90KGRmLGFlcyhuYW8pKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuNSwgY29sb3I9IndoaXRlIiwgZmlsbD0ic3RlZWxibHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBsYWJzKHggPSAiSWxvxZvEhyIsIHkgPSAiQ3rEmXN0b8WbxIciKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBvc2N5bGFjamkgcMOzxYJub2Nub2F0bGFudHlja2llaiAtIG5hbyIpIA0KICANCnBsb3RfbmFvX2hpc3QNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCiMgQW5hbGl6YSBicmFrdWrEhWN5Y2ggd2FydG/Fm2NpLg0KYGBge3IsIGVjaG89RkFMU0V9DQpucm93cyA8LSBucm93KGRmKQ0KbmNvbHMgPC0gbmNvbChkZikNCmNvbXBsZXRlX3Jvd3MgPC0gc3VtKGNvbXBsZXRlLmNhc2VzKHJhd19kZikpDQpuX25hX3Jvd3MgPC0gbnJvd3MgLSBjb21wbGV0ZV9yb3dzDQpwcm9wIDwtIDEwMCAqIG5fbmFfcm93cyAvIG5yb3dzDQpgYGANCg0KDQpCcmFrdWrEhWNlIHdhcnRvxZtjaSB3eXN0xJlwdWrEhSB0eWxrbyB3IDcgYXRyeWJ1dGFjaDogXA0KLSBkb3N0xJlwbm/Fm8SHIHBsYW5rdG9udSAtPiBjZmluMSwgY2ZpbjIsIGNoZWwxLCBjaGVsMiwgbGNvcDEsIGxjb3AyLCBcDQotIHRlbXBlcmF0dXJhIHByenkgcG93aWVyemNobmkgd29keSAtPiBzc3QuIFwNCsWBxIVjem5pZSBuaWVwZcWCbnljaCBvYnNlcndhY2ppIG1hbXk6IGByIG5fbmFfcm93c2AsIGN6eWxpIGplc3QgdG8gYHIgcHJvcGAgJSBjYcWCZWdvIHpiaWlvcnUuDQpKZXN0IHRvIHpuYWN6xIVjYSBjesSZxZvEhyB6YmlvcnUuIEplZG5hayB6YWvFgmFkYW15LCDFvGUgemJpw7NyIGRhbnljaCB6YXdpZXJhIGNocm9ub2xvZ2ljem5lIHXFgm/FvG9uZSBkYW5lLCBkbGF0ZWdvIHByb3N0ZSBvcGVyYWNqZSBuYSB3YXJ0b8WbY2lhY2ggcHVzdHljaCwgdGFraWUgamFrIMWbcmVkbmlhIGx1YiBtZWRpYW5hIHogZGFuZWdvIGF0cnlidXR1LCBtb2fEhSBjYcWCa293aWNpZSBwcnpla8WCYW1hxIcgbmFtIHJvemvFgmFkIGRhbnljaC4gWmUgd3pnbMSZZHUgbmEgaWxvxZvEhyB0eWNoIGRhbnljaCB1c3VuacSZY2llIGljaCBtb8W8ZSByw7NXbmllxbwgc3Bvd29kb3dhxIcgcsOzxbxuZSBza3V0a2kuIEplZG5hayBkemnEmWtpIHRlbXUgbW/FvG5hIHV6bmHEhywgxbxlIG9wZXJhY2plIGt0w7NyYSB6b3N0YcWCYSBkb2tvbmFuYSBuYSB3YXJ0b8WbY2lhY2ggcHVzdHljaCwgbWlhxYJhIGpha2lrb2x3aWVrIHBvenl0eXdueSBsdWIgbmVnYXR5d255IG5hIGRhbHN6ZSBwcnpldHdhcnphbmllLiANCmBgYHtyLCBlY2hvPVR9DQpkZiA8LSBkZl9ub19uYQ0KYGBgDQoNCiMgS29yZWxhY2phIG1pxJlkenkgYXRyeWJ1dGFtaQ0KYGBge3J9DQpjb3JyZWxhdGlvbiA8LSBjb3IoZGZfbm9fbmEpDQpjb3JycGxvdChjb3JyZWxhdGlvbix0eXBlPSJ1cHBlciIsdGwuY29sID0gImJsYWNrIiwgdGwuc3J0ID0gNDUpDQpgYGANCk5handpxJlrc3rEhSBrb3JlbGFjasSZIG1vxbxuYSB6YW9ic2Vyd293YcSHIG1pxJlkenkgcGFyYW1pOiBgY2hlbDFgIC0gYGxvb3AxYCAsYGNoZWwyYCAtIGBsb29wMmAsICBgZmJhcmAgLSBgY3VtZmAsIGBjZmluMmAgLSBgbGNvcDJgIG9yYXogYGN1bWZgIC0gYHRvdGFsbmAuDQpXIHN0b3N1bmt1IGRvIGF0cnlidXR1ICdsZW5ndGgnIG5hand5xbxzesSFIGtvcmVsYWNqZSBtb8W8bmEgemFvYnNlcndvd2HEhyB6ICBgc3N0YCwgYG5hb2AuDQpOYW8gamVzdCB0byBPc2N5bGFjamEgUMOzxYJub2Nub2F0bGFudHlja2EsIGt0w7NyYSBqZXN0IHpqYXdpc2tpZW0gbWV0ZW9yb2xvZ2ljem55bS4gV3lzdMSZcHVqZSB3IG9ic3phcnplIHDDs8WCbm9jbmVnbyBhdGxhbnR5a3UgaSBtYSB3cMWCeXcgbmEga2xpbWF0IG90YWN6YWrEhWN5Y2ggZ28ga29udHluZW50w7N3LiBKZWogZHppYcWCYWxub8WbxIcgendpxIV6YW5hIGplc3QgeiBjeXJrdWxhY2rEhSBwb3dpZXRyemEgaSB3b2R5LCBjbyBieSB0xYJ1bWFjennFgm8gemFsZcW8bm/Fm8SHIGTFgnVnb8WbY2kgxZtsZWR6aWEgb2QgdHljaCBkd8OzY2ggYXRyeWJ1dMOzdyBuYSByYXouIA0KYGxlbmd0aGAgamVzdCB0YWvFvGUgc2tvcmVsb3dhbnkgeiBpbnRlbnN5d25vxZtjaWEgcG/Fgm93w7N3IG9yYXogZG9zdMSZcG5vxZtjacSFIHBsYW5rdG9udSAoY2hlbDEgLSBDYWxhbnVzIGhlbGdvbGFuZGljdXMgZ2F0LiAxIGkgbGNvcDEgLSB3aWTFgm9ub2fDs3cgIGdhdC4gMSkuDQoNCg0KDQojIFptaWFuYSByb3ptaWFydSDFm2xlZHppYSB3IGN6YXNpZQ0KYGBge3J9DQpwYXJ0aXRpb24gPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5PWRmX25vX25hJGxlbmd0aCwgcD0uMDUsIGxpc3Q9RkFMU0UpDQpkZlBhcnRpdGlvbiA8LSBkZl9ub19uYVtwYXJ0aXRpb24sIF0NCnAgPC0gZ2dwbG90KGRmUGFydGl0aW9uLCBhZXMoeD1YLCB5PWxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArIHRoZW1lX2J3KCkNCmdncGxvdGx5KHApDQpgYGANCmBgYHtyfQ0KDQoNCmBgYA0KDQoNCiMgUmVncmVzb3IgDQpTZWtjasSZIHByw7NidWrEhWPEhSBzdHdvcnp5xIcgcmVncmVzb3IgcHJ6ZXdpZHVqxIVjeSByb3ptaWFyIMWbbGVkemlhICh3IHRlaiBzZWtjamkgbmFsZcW8eSB3eWtvcnp5c3RhxIcgd2llZHrEmSB6IHBvem9zdGHFgnljaCBwdW5rdMOzdyBvcmF6IHd5a29uYcSHIGRvZGF0a293ZSBjenlubm/Fm2NpLCBrdMOzcmUgbW9nxIUgcG9wcmF3acSHIHRyYWZub8WbxIcgcHJlZHlrY2ppKTsgZG9iw7NyIHBhcmFtZXRyw7N3IG1vZGVsdSBvcmF6IG9zemFjb3dhbmllIGplZ28gc2t1dGVjem5vxZtjaSBwb3dpbm55IHpvc3RhxIcgd3lrb25hbmUgemEgcG9tb2PEhSB0ZWNobmlraSBwb2R6aWHFgnUgemJpb3J1IG5hIGRhbmUgdWN6xIVjZSwgd2FsaWR1asSFY2UgaSB0ZXN0b3dlOyB0cmFmbm/Fm8SHIHJlZ3Jlc2ppIHBvd2lubmEgem9zdGHEhyBvc3phY293YW5hIG5hIHBvZHN0YXdpZSBtaWFyIFIyIGkgUk1TRS4NCg0KRGFuZSB6b3N0YcWCeSBwb2R6aWVsb25lIG5hIDIgemJpb3J5OiB6YmnDs3IgdHJlbmluZ293eSAtIHphd2llcmFqxIVjeSA4MCAlIGRhbnljaCwgb3JheiB6YmnDs3IgdGVzdG93eSAtIHphd2llcmFqxIVjeSAyMCAlIA0KYGBge3IsZWNobz1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcmZHcmlkIDwtIGV4cGFuZC5ncmlkKG10cnkgPSAxOjYpDQpjdHJsIDwtIHRyYWluQ29udHJvbCgNCiAgICBtZXRob2QgPSAicmVwZWF0ZWRjdiIsDQogICAgbnVtYmVyID0gNSwNCiAgICByZXBlYXRzID0gMikNCmBgYA0KDQpgYGB7cn0NCmFmdGVyRmVhdHVyZVNlbGVjdGlvbiA8LSBkZl9ub19uYSAlPiUgc2VsZWN0KGxlbmd0aCwgY2ZpbjEsIGNmaW4yLCBjaGVsMSwgY2hlbDIsIGxjb3AxLCBsY29wMiwgZmJhciwgcmVjciwgY3VtZiwgdG90YWxuLCBzc3QsIHNhbCwgbmFvKQ0KDQppblRyYWluaW5nIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeT1hZnRlckZlYXR1cmVTZWxlY3Rpb24kbGVuZ3RoLHA9MC44LGxpc3QgPSBGKQ0KdHJhaW5pbmcgPC0gYWZ0ZXJGZWF0dXJlU2VsZWN0aW9uW2luVHJhaW5pbmcsXQ0KdGVzdGluZyA8LSBhZnRlckZlYXR1cmVTZWxlY3Rpb25bLWluVHJhaW5pbmcsXQ0KWF90cmFpbiA9IHNlbGVjdCh0cmFpbmluZywgLWxlbmd0aCkNCnlfdHJhaW4gPSB0cmFpbmluZyRsZW5ndGgNClhfdGVzdCA9IHNlbGVjdCh0ZXN0aW5nLCAtbGVuZ3RoKQ0KeV90ZXN0ID0gdGVzdGluZyRsZW5ndGgNCmBgYA0KQ09TIE8gTU9ERUxVDQpgYGB7cn0NCmZpdCA8LSB0cmFpbihYX3RyYWluLHlfdHJhaW4sDQogICAgICAgICAgICBtZXRob2QgPSAncmYnLA0KICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmwsDQogICAgICAgICAgICAgbWV0cmljID0gIlJNU0UiLA0KICAgICAgICAgICAgIHR1bmVHcmlkPXJmR3JpZCwNCiAgICAgICAgICAgICBpbXBvcnRhbmNlID0gVFJVRSwNCiAgICAgICAgICAgICBudHJlZT0yMCkNCmdncGxvdChmaXQpICsgZ2d0aXRsZSgiT3B0eW1hbGl6YWNqYSBwYXJhbWV0cnUgbXRyeSBuYSBwb2RzdGF3aWUgbWlhcnkgUk1TRSIpICsgdGhlbWVfYncoKQ0KDQpgYGANCg0KU3ByYXdkemVuaWUgbW9kZWx1IG5hIGRhbnljaCB0ZXN0b3d5Y2gNCmBgYHtyIHRlc3RSRn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gWF90ZXN0KQ0KbW9kZWxWYWx1ZXMgPC0gZGF0YS5mcmFtZShvYnMgPSB5X3Rlc3QsIHByZWQgPSBwcmVkaWN0aW9ucykNCmZpdCRyZXN1bHRzDQpgYGANCg0KDQoNCg0KTW9kZWwgcmVncmVzeWpueSB6b3N0YcWCIHBvZGRhbnkgdGVzdG9tIG5hIGRhbnljaCB0ZXN0b3d5Y2guIE9zacSFZ25pxJl0byB3eW5payB3IHBvc3RhY2kgbWlhciBSTVNFIGkgUnNxdWFyZWQ6DQpgciBtb2RlbFZhbHVlc2ANCg0KYGBge3IgcmVnX3Bsb3RfcHJlZH0NCnhzIDwtIDE6bGVuZ3RoKHlfdGVzdCkNCmRhdGFmIDwtIGRhdGEuZnJhbWUoeHMseV90ZXN0KQ0KcmVnX3Bsb3RfdGVzdCA8LSBnZ3Bsb3QoZGF0YWYsYWVzKGRhdGFmJHhzLGRhdGFmJHlfdGVzdCkpICsgDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAwLjcsY29sb3I9ImJsdWUiKSArIA0KICBnZW9tX3Ntb290aChhZXMoeT1kYXRhZiR5X3Rlc3QsY29sb3VyID0gInRyZW5kIiksIHNpemUgPSAyKSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygxOSwgMzEpKSArDQogIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBsYWJzKHggPSAiTnIgcHJ6eWvFgmFkdSIsIHkgPSAiRMWCdWdvxZvEhyDFm2xlZHppYSAiKSArIA0KICBnZ3RpdGxlKCJXeWtyZXMgZMWCdWdvxZtjaSDFm2xlZHppYSBvZCBjemFzdSAtIGRhbmUgdGVzdG93ZSIpIA0KcmVnX3Bsb3RfdGVzdA0KYGBgDQpgYGB7cn0NCnhzIDwtIDE6bGVuZ3RoKHByZWRpY3Rpb25zKQ0KZGF0YWYgPC0gZGF0YS5mcmFtZSh4cyxwcmVkaWN0aW9ucykNCnJlZ19wbG90X3Rlc3QgPC0gZ2dwbG90KGRhdGFmLGFlcyhkYXRhZiR4cyxkYXRhZiRwcmVkaWN0aW9ucykpICsgDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAwLjcsY29sb3I9ImJsdWUiKSArIA0KICBnZW9tX3Ntb290aChhZXMoeT1kYXRhZiRwcmVkaWN0aW9ucyxjb2xvdXIgPSAidHJlbmQiKSwgc2l6ZSA9IDIpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDE5LCAzMSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGxhYnMoeCA9ICJOciBwcnp5a8WCYWR1IiwgeSA9ICJExYJ1Z2/Fm8SHIMWbbGVkemlhICIpICsgDQogIGdndGl0bGUoIld5a3JlcyBkxYJ1Z2/Fm2NpIMWbbGVkemlhIG9kIGN6YXN1IC0gcHJlZHlrY2phIikgDQpyZWdfcGxvdF90ZXN0DQpgYGANCg0KDQojIEFuYWxpemEgd2HFvG5vxZtjaSBhdHJ5YnV0w7N3DQpgYGB7cn0NCmltcG9ydGFuY2UgPC0gdmFySW1wKGZpdCwgc2NhbGUgPSBGQUxTRSkNCmdncGxvdChpbXBvcnRhbmNlKQ0KYGBgDQoNClogd3lrcmVzdSB3eW5pa2EsIMW8ZSBuYWp3YcW8bmllanN6eW0gYXRyeWJ1dGVtIGplc3Qgc3N0IC0gdGVtcGVyYXR1cmEgcHJ6eSBwb3dpZXJ6Y2huaSB3b2R5LiBKZWRuYWsga29sZWpuZSAzIG5handhxbxuaWVqc3plIGF0cnlidXR5IHRvIHBvemlvbSAzIHLDs8W8bnljaCByb2R6YWrDs3cgcGxhbmt0b251IC0gbGNvcDIsIGNmaW4yIG9yYXogY2hlbDIuIEplZG5hayB0cnplYmEgcGFtacSZdGHEhyBvIHd5c29raWVqIGtvcmVsYWNqaSBtacSZZHp5IGNmaW4yIGEgbG9vMiwgY28gbW9nxYJvIHphZGVjeWRvd2HEhywgxbxlIG9iYSBhdHJ5YnV0eSBzacSZIHpuYWxhesWCeSBqYWtvIG5handhxbxuaWVqc3plLiANCg0KIyMgWmFsZcW8bm/Fm8SHIGTFgnVnb8WbY2kgxZtsZWR6aWEgb2QgdGVtcGVyYXR1cnkgcHJ6eSBwb3dpZWNobmkNCmBgYHtyfQ0KZGZfc3N0X2xlbmd0aCA8LSBkZl9ub19uYSAlPiUgc2VsZWN0KFgsc3N0LGxlbmd0aCkNCmRmX3NzdF9sZW5ndGgyIDwtIG1lbHQoZGZfc3N0X2xlbmd0aCxpZC52YXJzID0gJ1gnKQ0KZmluX3Bsb3RfdGxlbiA8LSBnZ3Bsb3QoZGZfc3N0X2xlbmd0aDIsYWVzKHggPSBYLCB5PXZhbHVlKSkgKyANCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuNSwgY29sb3I9ImdyZXkiKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAyKSArDQogIGZhY2V0X2dyaWQodmFyaWFibGV+LiAsc2NhbGVzID0gJ2ZyZWUnKSAgKyB0aGVtZV9taW5pbWFsKCkgKyANCiAgbGFicyh4ID0gIkN6YXMiLCB5ID0gIlRlbXBlcmF0dXJhIC8gRMWCdWdvxZvEhyIpICsgDQogIHNjYWxlX3hfY29udGludW91cygpICsgDQogIGdndGl0bGUoIld5a3Jlc3kgdGVtcGVyYXR1cnkgcHJ6eSBwb3dpZWNobmkgd29keSBvcmF6IGTFgnVnb8WbY2kgxZtsZWR6aWEgb2QgY3phc3UiKSANCmZpbl9wbG90X3RsZW4NCg0KYGBgDQpgYGB7cn0NCmRmX3BsYW5jdCA8LSBkZl9ub19uYSAlPiUgc2VsZWN0KFgsbGNvcDIsY2ZpbjIsY2hlbDIsbGVuZ3RoKQ0KZGZfcGxhbmN0MiA8LSBtZWx0KGRmX3BsYW5jdCxpZC52YXJzID0gJ1gnKQ0KZmluX3Bsb3RfdGxlbiA8LSBnZ3Bsb3QoZGZfcGxhbmN0MixhZXMoeCA9IFgsIHk9dmFsdWUpKSArIA0KICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC41LCBjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDIpICsNCiAgZmFjZXRfZ3JpZCh2YXJpYWJsZX4uICxzY2FsZXMgPSAnZnJlZScpICArIHRoZW1lX21pbmltYWwoKSArIA0KICBsYWJzKHggPSAiQ3phcyIsIHkgPSAiUGxhbnRrb24gLyBExYJ1Z2/Fm8SHIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKCkgKyANCiAgZ2d0aXRsZSgiV3lrcmVzeSBpbG/Fm2NpIHBsYW5rdG9udSBvcmF6IGTFgnVnb8WbY2kgxZtsZWR6aWEgb2QgY3phc3UiKSANCmZpbl9wbG90X3RsZW4NCmBgYA0KDQojIFduaW9za2kNCg0KDQojIEludGVyYWt0eXdueSBXeWtyZXMNClcgY2VsdSB3eWdlbmVyb3dhbmlhIGludGVyYWt0eXduZWdvIHd5a3Jlc3UgemEgcG9tb2PEhSBiaWJsaW90ZWtpIHNoaW55LCBkYW5lIHoga29sdW1ueSBsZW5naHQgem9zdGHFgnkgcG9ncnVwb3dhbmUgcG8gNTAga29sZWpueWNoIGVsZW1lbnTDs3csIGEgeiB0YWtpY2ggZ3J1cCBwb2xpY3pvbmEgxZtyZWRuaWEuIFd5a29uYW5vIHRvIGJ5ICJzdXdhayIgbmEgd3lrcmVzaWUgbmllIGJ5xYIgemEgZMWCdWdpLiBOaWVzdGV0eSBiaWJsaW90ZWthIHNoaW55IG5pZSBkYWplIG1vxbxsaXdvxZtjaSBuYSB3eWtvcnp5c3RhbmllIGplaiB3IHN0YXR5Y3pueW0gcGxpa3UgcmFwb3J0dSwgZGxhdGVnbyBrb2QgZG8gd3lrb25hbmlhIHRha2llZ28gd3lrcmVzdSB6bmFqZHVqZSBzacSZIHcgcGxpa3UgImxpYnJhcnkuUiIu